#pragma once

#include <iostream>
#include <cassert>
#include <pthread.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <queue>
#include <memory>
#include "Lock.hpp"
#include "Log.hpp"

using namespace std;

int gThreadNum = 5;

template<typename T>
class ThreadPool
{
private:
    bool _isStart;
    int _threadNum;
    queue<T> _taskQueue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
    
    static ThreadPool<T>* instance;
private:
    ThreadPool(int num = gThreadNum)
        :_threadNum(num), _isStart(false)
    {
        assert(_threadNum > 0);
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
    ThreadPool(const ThreadPool<T>&) = delete;
    void operator=(const ThreadPool<T>&) = delete;
public:
    static ThreadPool<T>* getInstance()
    {
        static Mutex mutex;
        if (nullptr == instance)
        {
            LockGuard lockguard(&mutex);
            if (nullptr == instance)
            {
                instance = new ThreadPool<T>;
            }
        }
        return instance;
    }

    static void* threadRoutine(void* args)
    {
        pthread_detach(pthread_self());
        ThreadPool<T>* tp = static_cast<ThreadPool<T>*>(args);
        prctl(PR_SET_NAME, "follower");
        while (1)
        {
            tp->lockQueue();
            while (!tp->haveTask())
            {
                tp->waitForTask();
            }
            T t = tp->pop();
            tp->unlockQueue();

            int one, two;
            char oper;
            t.get(&one, &two, &oper);
            Log() << "新线程完成计算任务: " << one << oper << two 
                << "=" << t.run() << std::endl;
        }
    }

    void start()
    {
        assert(!_isStart);
        for (int i = 0; i < _threadNum; ++i)
        {
            pthread_t temp;
            pthread_create(&temp, nullptr, threadRoutine, this);
        }
        _isStart = true;
    }

    void push(const T& in)
    {
        lockQueue();
        _taskQueue.push(in);
        choiceThreadForHandler();
        unlockQueue();
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
private:
    void lockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void unlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    bool haveTask()
    {
        return !_taskQueue.empty();
    }
    void waitForTask()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void choiceThreadForHandler()
    {
        pthread_cond_signal(&_cond);
    }
    T pop()
    {
        T temp = _taskQueue.front();
        _taskQueue.pop();
        return temp;
    }
};

template<typename T>
ThreadPool<T>* ThreadPool<T>::instance = nullptr;
